package com.linln.admin.app.service.task;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linln.admin.app.constants.AppConstants;
import com.linln.admin.app.constants.TaskTypeEnum;
import com.linln.admin.app.domain.AppInfo;
import com.linln.admin.app.domain.AppVersion;
import com.linln.admin.app.domain.RepoAccount;
import com.linln.admin.app.domain.VersionApp;
import com.linln.admin.app.service.branch.BranchHelper;
import com.linln.admin.system.domain.TaskQueue;
import com.linln.admin.system.repository.TaskQueueRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.Pager;
import org.gitlab4j.api.ProjectApi;
import org.gitlab4j.api.models.Project;
import org.gitlab4j.api.models.ProjectFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 已发布操作
 */
@Service
@Slf4j
public class PublishedTask extends AbstractTaskExecutor {
    @Autowired
    private TaskQueueRepository taskQueueRepository;

    /**
     * 是否可以处理这个任务
     *
     * @param t
     * @return
     */
    @Override
    public boolean support(TaskQueue t) {
        return t != null && TaskTypeEnum.PUBLISHED.getType().equals(t.getType());
    }

    /**
     * 处理任务，并返回处理结果。将release代码合并到prod，然后将prod合并到现在所有在开发的代码中
     *
     * @param t
     * @return true-成功; 其它-false
     */
    @Override
    public boolean exec(TaskQueue t) {
        Long versionId = t.getRelate_id();
        Optional<AppVersion> ver = versionRepository.findById(versionId);
        if (!ver.isPresent()) {
            throw new IllegalArgumentException("对应的版本信息不存在！");
        }
        AppVersion version = ver.get();

        String appIdJson = t.getData();
        List<VersionApp> vas = new ArrayList<>();
        if (StringUtils.isBlank(appIdJson)) {//将这个版本中所有关联的项目查询出来
            vas = versionAppRepository.findByVersionId(versionId);
            if (vas == null || vas.isEmpty()) {
                return true;
            }
        } else {
            try {//将指定要合并的信息查询出来
                List<Long> appIds = new ObjectMapper().readValue(appIdJson, new TypeReference<List<Long>>() {
                });
                if (appIds == null || appIds.isEmpty()) {
                    return true;
                }
                List<VersionApp> tmp = versionAppRepository.findByVersionId(versionId);
                if (tmp == null || tmp.isEmpty()) {
                    return true;
                }
                for (VersionApp va : tmp) {
                    if (appIds.contains(va.getApp_id())) {
                        vas.add(va);
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }

        }

        String baseVer = version.getVersion().trim().replace(" ", "");
        if (baseVer.startsWith(AppConstants.BRANCH_DEV_PREFIX)) {
            baseVer = baseVer.substring(4);
        }
        String release = AppConstants.BRANCH_RELEASE_PREFIX + baseVer;
        if (AppConstants.YES.equals(version.getHotfix_flag())) {
            release = AppConstants.BRANCH_HOTFIX_PREFIX + baseVer;
        }

        List<String> baseUrls = new ArrayList<>();//项目基线URL
        Map<AppInfo, RepoAccount> appAccountMap = new HashMap<>();
        for (VersionApp va : vas) { //将代码合并到基线分支
            if (va == null || va.getApp_id() == null) {
                continue;
            }
            if (AppConstants.NO.equals(va.getHas_release()) && AppConstants.NO.equals(version.getHotfix_flag())) {
                continue;//如果不是修复分支且没有release分支，则不处理
            }
            Optional<AppInfo> app = infoRepository.findById(va.getApp_id());
            if (!app.isPresent()) {
                versionAppRepository.delete(va);
                continue;
            }
            AppInfo info = app.get();
            if (!baseUrls.contains(info.getRepo_url())) {
                baseUrls.add(info.getRepo_url());
            }
            try {//将release合并到prod
                RepoAccount account = getRepoAccount(info.getRepo_account_id());
                if (BranchHelper.findHandler(info.getRepo_type()).mergeBranch(baseVer, info.getRepo_url(), account.getAccount(), account.getPass(), release, info.getBase())) {
                    va.setMerge_to_status(AppConstants.VERSION_MERGE_STATUS_AUTO_MERGED);
                    appAccountMap.put(info, account);
                } else {//理论上来说，不会走到这个else分支中
                    va.setMerge_to_status(AppConstants.VERSION_MERGE_STATUS_CONFLICT);
                }
                va.setMerge_to_time(new Date());
            } catch (Throwable e) {
                String errMsg = info.getDisplay_name() + " 从" + release + "合并到" + info.getBase() + "分支时出错了：" + e.getMessage();
                t.setMsg(StringUtils.trimToEmpty(t.getMsg() + "\n" + errMsg));
                log.error(errMsg, e);
            }
            versionAppRepository.save(va);
        }
        try {
            log.info("notify kooder refresh: " + appAccountMap);
            noticeKooder(appAccountMap);
        } catch (Exception ignore) {
        }

        mergeProdToAllDev(version, vas, baseUrls);
        return true;
    }

    /**
     * 通过代码搜索平台更新索引
     *
     * @param appAccountMap
     */
    private void noticeKooder(Map<AppInfo, RepoAccount> appAccountMap) throws GitLabApiException, JsonProcessingException {
        if (appAccountMap == null || appAccountMap.isEmpty()) {
            return;
        }

        //先查询所有的应用
        Map<String, CodeRepository> prjs = new HashMap<>();
        GitLabApi gitlab = Gitlab.INSTANCE;
        int maxId = 0;
        //list all repositories with id bigger than {maxId}
        ProjectApi api = gitlab.getProjectApi();
        Pager<Project> projects = api.getProjects(new ProjectFilter().withIdAfter(maxId), 200);
        while (projects.hasNext()) {
            for (Project p : projects.next()) {
                CodeRepository codeRepo = new CodeRepository();
                codeRepo.setEnterprise(0); //Gitlab doesn't support enterprise
                codeRepo.setId(p.getId());
                codeRepo.setName(p.getName());
                codeRepo.setUrl(p.getWebUrl());
                String name = FilenameUtils.getName(p.getWebUrl());
                if (name.endsWith(".git")) {
                    name = name.substring(0, name.length() - 4);
                }
                prjs.put(name, codeRepo);
            }
        }

        List<CodeRepository> codes = new ArrayList<>();
        for (AppInfo info : appAccountMap.keySet()) {
            if (info == null || info.getId() == null) {
                continue;
            }
            //查找gitlab上对应的id，并推送kooder
            String name = FilenameUtils.getName(info.getRepo_url());
            if (name.endsWith(".git")) {
                name = name.substring(0, name.length() - 4);
            }
            if (!prjs.containsKey(name)) {
                //没有对应的名称，则不处理
                continue;
            }
            if (prjs.get(name) == null) {
                continue;
            }

            codes.add(prjs.get(name));
        }
        log.info("notify kooker projects :" + codes);
        HttpRequest req = HttpUtil.createRequest(Method.PATCH, Gitlab.getKooderUrl());
        req.setConnectionTimeout(10000).setRest(true).body(new ObjectMapper().writeValueAsString(codes));
        HttpResponse res = req.execute(true);
        try {
            log.info("notify kooder result:" + res.getStatus());
        } finally {
            res.close();
        }
    }

    public static void main(String[] args) throws GitLabApiException, JsonProcessingException {
        //先查询所有的应用
        List<CodeRepository> codes = new ArrayList<>();
        GitLabApi gitlab = Gitlab.INSTANCE;
        int maxId = 0;
        //list all repositories with id bigger than {maxId}
        ProjectApi api = gitlab.getProjectApi();
        Pager<Project> projects = api.getProjects(new ProjectFilter().withIdAfter(maxId), 20);
        while (projects.hasNext()) {
            for (Project p : projects.next()) {
                CodeRepository repo = new CodeRepository();
                repo.setEnterprise(0); //Gitlab doesn't support enterprise
                repo.setId(p.getId());
                repo.setName(p.getName());
                repo.setUrl(p.getWebUrl());
                codes.add(repo);
            }
        }

        HttpRequest req = HttpUtil.createRequest(Method.PATCH, Gitlab.getKooderUrl());
        req.setConnectionTimeout(10000).setRest(true).body(new ObjectMapper().writeValueAsString(codes));
        HttpResponse res = req.execute(true);
        try {
            log.info("notify kooder result:" + res.body());
        } finally {
            res.close();
        }
    }

    /**
     * 所有还在开发的分支，即：没有上线的版本
     *
     * @param published
     * @param publishedApps
     */
    private void mergeProdToAllDev(AppVersion published, List<VersionApp> publishedApps, List<String> baseUrls) {
        //这里不用分页，是因为正常情况下，不会有太多版本处于开发中
        List<AppVersion> devs = versionRepository.findByPublished(AppConstants.NO);
        if (devs == null || devs.isEmpty()) {
            return;
        }
        List<Long> appIds = new ArrayList<>();
        for (VersionApp va : publishedApps) {
            appIds.add(va.getApp_id());
        }
        for (AppVersion dev : devs) {
            Long versionId = dev.getId();
            if (versionId.compareTo(published.getId()) == 0) {
                continue;//如果是刚刚发布的这个，就要操作了。
            }
            List<VersionApp> vas = versionAppRepository.findByVersionId(versionId);
            if (vas == null || vas.isEmpty()) {
                continue;
            }

            String baseVer = dev.getVersion().trim().replace(" ", "");
            if (baseVer.startsWith(AppConstants.BRANCH_DEV_PREFIX)) {
                baseVer = baseVer.substring(4);
            }
            String devBranch = AppConstants.BRANCH_DEV_PREFIX + baseVer;
            if (AppConstants.YES.equals(dev.getHotfix_flag())) {
                devBranch = AppConstants.BRANCH_HOTFIX_PREFIX + baseVer;
            }
            List<Long> releaseRecIds = new ArrayList<>();
            for (VersionApp va : vas) {
                if (va == null || va.getApp_id() == null) {
                    continue;
                }
                if (AppConstants.VERSION_MERGE_STATUS_CONFLICT.equals(va.getMerge_from_status())) {
                    continue;//如果已经有冲突，则不处理
                }
                Optional<AppInfo> app = infoRepository.findById(va.getApp_id());
                if (!app.isPresent()) {
                    versionAppRepository.delete(va);
                    continue;
                }
                AppInfo info = app.get();


                //如果不是已发布的项目，不用合并
                if (!(appIds.contains(va.getApp_id()) || baseUrls.contains(info.getRepo_url()))) {
                    continue;
                }

                try {//prod合并到dev
                    RepoAccount account = getRepoAccount(info.getRepo_account_id());
                    if (BranchHelper.findHandler(info.getRepo_type()).mergeBranch(baseVer, info.getRepo_url(), account.getAccount(), account.getPass(), info.getBase(), devBranch)) {
                        va.setMerge_from_status(AppConstants.VERSION_MERGE_STATUS_AUTO_MERGED);
                        if (AppConstants.NO.equals(dev.getHotfix_flag()) && AppConstants.YES.equals(dev.getCo_release_branch())) {
                            //如果当前版本不是修复版本，同时已经创建了发布分支，则需要创建dev合并到release的任务
                            releaseRecIds.add(va.getId());
                        }
                    } else {
                        va.setMerge_from_status(AppConstants.VERSION_MERGE_STATUS_CONFLICT);
                    }
                    va.setMerge_from_time(new Date());
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                versionAppRepository.save(va);
            }

            if (releaseRecIds.size() > 0) {//如果当前有发布分支，则创建合并到发布分支的任务
                try {
                    TaskQueue tq = new TaskQueue();
                    tq.setRelate_id(versionId);
                    tq.setType(TaskTypeEnum.MERGE_DEV_RELEASE.getType());
                    tq.setStatus(AppConstants.TASK_STATUS_NEW);
                    tq.setData(new ObjectMapper().writeValueAsString(releaseRecIds));
                    tq.setCreate_date(new Date());
                    taskQueueRepository.save(tq);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
    }
}
